/* @flow */

import config from '../config'
import {initProxy} from './proxy'
import {initState} from './state'
import {initRender} from './render'
import {initEvents} from './events'
import {mark, measure} from '../util/perf'
import {initLifecycle, callHook} from './lifecycle'
import {initProvide, initInjections} from './inject'
import {extend, mergeOptions, formatComponentName} from '../util/index'

let uid = 0

// 接收vue实例
export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this

    // a uid
    // 每个vue实例都有递增的uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      // 子组件初始化
      initInternalComponent(vm, options)
    } else {
      // 根组件初始化
      // 合并全局配置到根组件的局部配置
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      // 设置代理，将vm实例上的属性代理到vm._renderProxy
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm

    // 初始化组件实例关系属性，比如 $parent $children $root $refs 等
    initLifecycle(vm)

    // 初始化自定义事件
    // 在子组件上注册的事件，监听者为子组件本身，与父组件无关
    initEvents(vm)

    // 解析组件的插槽信息，得到vm.$slot，处理渲染函数，得到vm.$createElement方法，即h函数
    initRender(vm)

    // 调用beforeCreate钩子函数
    callHook(vm, 'beforeCreate')

    // 初始化组件的inject配置，得到result[key] = val 形式的配置对象，然后对结果数据进行响应式处理，并代理每个key到vm实例
    initInjections(vm) // resolve injections before data/props

    // 响应式关键，处理 props，methods，data，computed，watch
    initState(vm)

    // 解析组件配置项上的 provide 对象，将其挂载到 vm._provided 属性上
    initProvide(vm) // resolve provide after data/props

    // 调用created钩子函数
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    // 判断配置项上是否有el选项，则自动调用$mount方法。
    // 无el选项，则需手动调用$mount方法
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

export function initInternalComponent(vm: Component, options: InternalComponentOptions) {
  const opts = vm.$options = Object.create(vm.constructor.options)
  // doing this because it's faster than dynamic enumeration.
  const parentVnode = options._parentVnode
  opts.parent = options.parent
  opts._parentVnode = parentVnode

  const vnodeComponentOptions = parentVnode.componentOptions
  opts.propsData = vnodeComponentOptions.propsData
  opts._parentListeners = vnodeComponentOptions.listeners
  opts._renderChildren = vnodeComponentOptions.children
  opts._componentTag = vnodeComponentOptions.tag

  if (options.render) {
    opts.render = options.render
    opts.staticRenderFns = options.staticRenderFns
  }
}

// 初始化根组件
export function resolveConstructorOptions(Ctor: Class<Component>) {
  // 配置项目
  let options = Ctor.options

  if (Ctor.super) {
    // 判断是否存在基类，有则递归解析基类构造函数的选项
    const superOptions = resolveConstructorOptions(Ctor.super)
    // 将
    const cachedSuperOptions = Ctor.superOptions
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      // 基类构造函数发生变化，需要重新设置
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      // 检查Ctor.options上是否有任何后期修改/附加的选项
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      // 有则合并选项
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }

      // 将合并结果给Ctor.options
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

// 解析构造函数选项中后续被修改或添加的选项
function resolveModifiedOptions(Ctor: Class<Component>): ?Object {
  let modified

  // 构造函数选项
  const latest = Ctor.options

  // 密封的构造函数选项，备份
  const sealed = Ctor.sealedOptions

  // 对比两个选项，记录不一致的选项
  for (const key in latest) {
    if (latest[key] !== sealed[key]) {
      if (!modified) modified = {}
      modified[key] = latest[key]
    }
  }
  return modified
}
